<?php

namespace App\Models\Customer;

use App\Models\Admin\CustomerAdmin;
use App\Models\Admin\StoreAdmin;
use App\Models\Concerns\ScopeFilterable;
use App\Models\Concerns\YouzanAuthorizerable;
use App\Models\Coupon\CouponTicket;
use App\Models\OpenWechat\Authorizer;
use App\Models\OpenWechat\WechatUser;
use App\Models\Order;
use App\Models\Panx\Brand;
use App\Models\Panx\Category;
use App\Models\Panx\CategoryPivotCustomer;
use App\Models\Panx\Product;
use App\Models\Store\PackCategory;
use App\Models\Store\SkinRecord;
use App\Models\Store\Store;
use App\Models\Store\StorePivotWechatUser;
use App\Models\Youzan\YouzanAuthorizer;
use App\Observers\BosCleanObserver;
use App\Services\Locale\Language;
use App\Models\Customer\Language as ModelLanguage;
use App\Services\Utils\Bosable;
use App\Services\Utils\Guard;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Http\UploadedFile;

/**
 * @property int id
 * @property int provider_type
 * @property int is_deleted
 * @property int $has_hair
 * @property int dev_acnt_num
 * @property int authorizer_id
 * @property int has_youzan
 * @property string account
 * @property string water_mark
 * @property string name
 * @property string youzan_authorizer_id
 *
 * @property-read Authorizer authorizer
 * @property-read YouzanAuthorizer youzanAuthorizer
 * @property-read Collection|CustomerAdmin[] customerAdmins
 * @property-read Collection|CustomerAdmin[] oldestSuperAdmins
 * @property-read Collection|Store[] stores
 * @property-read Collection|Category[] categories
 * @property-read Collection|PackCategory[] packCategories
 * @property-read Collection|DeviceAccount[] deviceAccounts
 * @property-read Collection|Product[] selfProducts
 * @property-read Collection|Product[] products
 * @property-read Collection|SkinRecord[] $skinRecords
 * @property-read Collection|WechatUser[] $noAuthorizerWechatUsers
 * @property-read Collection|WechatUser[] $storeWechatUserPivots
 * @property-read Collection|CouponTicket[] couponTickets
 *
 * @property-write string language
 * @property-write CustomerAdmin admin
 *
 * @method static self filterProviderType($provider_type)
 * @method static self filterShowVirtual($show_virtual)
 * @method static self filterIsDeleted($is_deleted)
 * @method static self filterHasHair($has_hair)
 * @method static self filterAccount(?string $account)
 * @method static self filterIsDebug(? $is_debug)
 *
 * @mixin Builder
 */
class Customer extends Model implements YouzanAuthorizerable
{
    use Bosable, ScopeFilterable;

    const IS_NOT_DELETED = 0; // 代理客户 启用
    const IS_DELETED = 1;    // 代理客户 禁用

    const PROVIDER_TYPE_VIRTUAL = 1;  // 虚拟供应商
    const PROVIDER_TYPE_NORMAL = 2;   // 普通客户
    const PROVIDER_TYPE_SOFTWARE = 3;    // 购买软件的客户

    const IS_DEBUG = 1;  // 是测试客户

    // 新鸿基项目参数： 客户的类型， 1：开启试妆/开启测肤。2：开启试妆/关闭测肤。3：关闭试妆/开启测肤
    const CUSTOMER_TYPE_ALL = 1;
    const CUSTOMER_TYPE_MAKEUP = 2;
    const CUSTOMER_TYPE_SKIN_DETECTOR = 3;

    const ID_FOREIGN_KEY_DEFAULT = 0; // 关联表无客户的默认 ID
    const ID_SUN_HUNG_KAI = 174;
    const ID_ADMIN = 1;

    const SPECIFY_CUSTOMER_IDS = [ self::ID_SUN_HUNG_KAI ]; // 客户类型是2（开启试妆/开启测肤），指定客户的customer_ids

    /**
     * 可用的商家类型
     */
    const VALIDATE_PROVIDER_TYPES = [
        self::PROVIDER_TYPE_NORMAL,
        self::PROVIDER_TYPE_VIRTUAL
    ];

    const HAS_YOUZAN = 1;                // 开通有赞的商品
    const NO_YOUZAN = 0;                 // 没有有赞的商品

    const HIDE_VIRTUAL = 0;      // 关闭虚拟仓
    const SHOW_VIRTUAL = 1;       // 开启虚拟仓

    const HIDE_PANX_BANNER_IDS = [ // 不显示 Panx 的植入广告 banner 的客户 ID
        132, // 魔妆镜展会客户
        136, // 欧华蒂斯
    ];

    public $bos_columns = ['license', 'water_mark'];

    protected $license_bos_key_prefix = 'customer/license/';
    protected $water_mark_bos_key_prefix = 'customer/water_mark/';

    protected static $unguarded = true;

    protected $table = 'customers';

    protected $casts = [
        'provider_type'    => 'int',
        'is_deleted'       => 'int',
        'has_youzan'       => 'int',
        'authorizer_id'    => 'int',
        'division_percent' => 'float',
        'dev_acnt_num'     => 'int',
        'id'               => 'int',
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|Category
     */
    public function categories()
    {
        return $this->belongsToMany(Category::class, 'category_pivot_customer', 'customer_id', 'category_id')
            ->withPivot('priority', 'is_show', 'id');
    }


    /**
     * @used-by $packCategories
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function packCategories()
    {
        return $this->hasMany(PackCategory::class,'customer_id', 'id');
    }

    /**
     * @used-by $authorizer
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function authorizer()
    {
        return $this->belongsTo(Authorizer::class);
    }

    /**
     * @used-by $youzanAuthorizer
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|YouzanAuthorizer
     */
    public function youzanAuthorizer()
    {
        return $this->belongsTo(YouzanAuthorizer::class);
    }

    /**
     * @used-by $noAuthorizerWechatUsers
     * 客户（无授权公众号） <== 微信用户
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function noAuthorizerWechatUsers()
    {
        return $this->hasMany(WechatUser::class, 'customer_id', 'id');
    }

    /**
     * 通过 customer <== store <== store_pivot_wechat_user 的关系可以确定一个客户有多少微信用户
     * （store_pivot_wechat_user 表中同客户下不同店铺可能有相同的微信用户ID，需要过滤）
     *
     * @used-by $storeWechatUserPivots
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
     */
    public function storeWechatUserPivots()
    {
        return $this->hasManyThrough(
            StorePivotWechatUser::class, Store::class,
            'customer_id', // Foreign key on stores table...
            'store_id', //  Foreign key on store_pivot_wechat_user table...
            'id', // Local key on customers table
            'id' // Local key on stores table
        );
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function virtualProducts()
    {
        return $this->belongsToMany(Product::class, 'customer_pivot_virtual_product', 'customer_id', 'product_id')
            ->where('is_sale', Product::IS_SALE)
            ->where('is_delete', Product::IS_NO_DELETE);
    }

    /**
     * @used-by $products
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function products()
    {
        return $this->hasMany(Product::class, 'customer_id', 'id');
    }

    /**
     * @used-by $skinRecords
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
     */
    public function skinRecords()
    {
        return $this->hasManyThrough(SkinRecord::class, Store::class);
    }

    /**
     * @deprecated - use selfProducts() instead
     *
     * @return \Illuminate\Database\Eloquent\Relations\hasMany |Product
     */
    public function getProducts()
    {
        return $this->selfProducts();
    }

    /**
     * 客户自有商品
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|Product
     */
    public function selfProducts()
    {
        return $this->hasMany(Product::class, 'customer_id', 'id')
            ->where('store_id', Product::STORE_ID_CUSTOMER);
    }

    /**
     * 客户同步的有赞商品，产生的订单。
     *
     * @return Builder|\Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function orders()
    {
        return $this->hasMany(Order::class, 'customer_id', 'id')
            ->where('product_belong_to', Order::PRODUCT_BELONG_TO_CUSTOMER);
    }

    /**
     * 客户下店铺的商品
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|Product
     */
    public function storeProducts()
    {
        return $this->hasMany(Product::class, 'customer_id', 'id')
            ->where('store_id', '<>', Product::STORE_ID_CUSTOMER);
    }

    /**
     * @param $license
     * @throws \Exception
     */
    public function setLicenseAttribute($license)
    {
        if (is_file($license)) {
            $this->attributes['license'] = $this->uploadPostFile($license, $this->license_bos_key_prefix);
        } else {
            $this->attributes['license'] = $license;
        }
    }

    /**
     * @return mixed|null
     */
    public function getLicenseAttribute()
    {
        if (!$this->attributes['license']) return null;

        return $this->getStaticUrl($this->attributes['license']);
    }

    /**
     * @param UploadedFile|string|null $water_mark
     * @throws \Exception
     */
    public function setWaterMarkAttribute($water_mark)
    {
        if (is_file($water_mark)) {
            $this->attributes['water_mark'] = $this->uploadPostFile($water_mark, $this->water_mark_bos_key_prefix);
        } else {
            $this->attributes['water_mark'] = $water_mark;
        }
    }

    /**
     * @return mixed|null
     */
    public function getWaterMarkAttribute()
    {
        if (!$this->attributes['water_mark']) return null;

        return $this->getStaticUrl($this->attributes['water_mark']);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function stores()
    {
        return $this->hasMany(Store::class, 'customer_id', 'id');
    }

    /**
     * @return array
     */
    public function getStoreIds(): array
    {
        return $this->stores()->pluck('id')->all();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|Advert
     */
    public function adverts()
    {
        return $this->hasMany(Advert::class, 'customer_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|CustomerAdmin
     */
    public function customerAdmins()
    {
        return $this->hasMany(CustomerAdmin::class, 'customer_id', 'id');
    }

    /**
     * @used-by $oldestSuperAdmins
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|CustomerAdmin
     */
    public function oldestSuperAdmins()
    {
        return $this->customerAdmins()
            ->whereHas('roles', function (Builder $query) {
                $query->where('name', CustomerAdmin::SUPER_MAN)
                    ->where('guard_name', CustomerAdmin::GUARD_NAME);
            })
            ->oldest();
    }

    /**
     * @used-by $deviceAccounts
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|DeviceAccount
     */
    public function deviceAccounts()
    {
        return $this->hasMany(DeviceAccount::class, 'customer_id', 'id');
    }

    /**
     * @used-by filterProviderType
     *
     * @param Builder $query
     * @param $provider_type
     * @return Builder
     */
    public function scopeFilterProviderType(Builder $query, $provider_type)
    {
        if (!$provider_type) return $query;

        return $query->where('provider_type', $provider_type);
    }

    /**
     * @used-by filterShowVirtual
     *
     * @param Builder $query
     * @param $show_virtual
     * @return Builder
     */
    public function scopeFilterShowVirtual(Builder $query, $show_virtual)
    {
        if (!isset($show_virtual)) return $query;

        return $query->where('show_virtual', $show_virtual);
    }

    /**
     * @used-by filterIsDeleted
     * @param Builder $query
     * @param $is_deleted
     * @return Builder
     */
    public function scopeFilterIsDeleted(Builder $query, $is_deleted)
    {
        if (!isset($is_deleted)) return $query;

        return $query->where('is_deleted', $is_deleted);
    }

    /**
     * @used-by filterHasHair
     *
     * @param Builder $query
     * @param $has_hair
     * @return Builder
     */
    public function scopeFilterHasHair(Builder $query, $has_hair)
    {
        if (!isset($has_hair)) return $query;

        return $query->where('has_hair', $has_hair);
    }

    /**
     * @used-by filterIsDebug
     *
     * @param Builder $query
     * @param $is_debug
     * @return Builder
     */
    public function scopeFilterIsDebug(Builder $query, $is_debug)
    {
        if (is_null($is_debug)) return $query;

        return $query->where('is_debug', $is_debug);
    }

    /**
     * @used-by filterAccount()
     *
     * @param Builder $query
     * @param $account
     *
     * @return Builder
     */
    public function scopeFilterAccount(Builder $query, ?string $account)
    {
        if (!isset($account)) return $query;

        // orWhereHas : select * from `customers` where (`name` LIKE ? or `legal_person` LIKE ? or `telephone` LIKE ? or `contacts` LIKE ? or `phone` LIKE ?) or (exists (select * from `customer_admins` where `customers`.`id` = `customer_admins`.`customer_id` and `account` LIKE ?)) order by `created_at` desc limit 15 offset 0 | 27ms
        // orWhereIn select * from `customers` where (`name` LIKE ? or `legal_person` LIKE ? or `telephone` LIKE ? or `contacts` LIKE ? or `phone` LIKE ?) or (`id` in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)) order by `created_at` desc limit 15 offset 0 | 21ms

        /*return $query->orWhereHas('customerAdmins',function (Builder $query) use ($keyword) {
            $query->where('account', 'LIKE', '%' . $keyword . '%');
        });*/

        $customer_ids = (new CustomerAdmin())
            ->where('account', 'LIKE', '%' . $account . '%')
            ->pluck('customer_id');

        return $query->orWhereIn('id', $customer_ids);
    }

    /**
     * @return bool
     */
    public function isProviderTypeSoftware(): bool
    {
        return $this->isProviderType(self::PROVIDER_TYPE_SOFTWARE);
    }

    /**
     * @return bool
     */
    public function isProviderTypeVirtualProvider(): bool
    {
        return $this->isProviderType(self::PROVIDER_TYPE_VIRTUAL);
    }

    /**
     * @return bool
     */
    public function isProviderTypeNormal(): bool
    {
        return $this->isProviderType(self::PROVIDER_TYPE_NORMAL);
    }

    public function isProviderType($type): bool
    {
        return $this->provider_type === $type;
    }

    /**
     * 注册删除 bosKey 事件绑定
     */
    protected static function boot()
    {
        parent::boot();

        /*
         * 监听bos清理事件
         */
        self::observe(BosCleanObserver::class);
    }

    /**
     * 最先开通的超管
     *
     * @return CustomerAdmin|null
     */
    public function firstSuperAdmin(): ?CustomerAdmin
    {
        /**
         * @see CustomerAdmin::scopeRole()
         */
        return $this->customerAdmins()
            ->role(CustomerAdmin::SUPER_MAN)
            ->oldest('created_at')
            ->first();
    }

    public function isAuthorizerChanged(int $new_author_id): bool
    {
        return $this->authorizer_id !== $new_author_id;
    }

    public function getAllStoreAdminIds()
    {
        $store_ids = $this->stores->pluck('id')->all();

        $all_store_admin_id = StoreAdmin::whereIn('store_id', $store_ids)->pluck('id')->all();

        return $all_store_admin_id;
    }

    /**
     * @return array|int[]
     */
    public function selfIsSaleProductIds(): array
    {
        return $this->selfProducts()
//            ->where('is_deleted',Store::IS_NOT_DELETED)
            ->where('is_sale', Product::IS_SALE)
            ->pluck('id')
            ->all();
    }

    /**
     * 需要显示植入广告
     */
    public function showPanxBanner(): bool
    {
        return !in_array($this->getKey(), self::HIDE_PANX_BANNER_IDS);
    }

    /**
     * 客户是否是新鸿基
     */
    public function isSunHungKai(): bool
    {
        return (bool)((int)$this->getKey() === self::ID_SUN_HUNG_KAI);
    }

    /**
     * 返回所有的供货商
     *
     * @return self[]|Collection
     */
    public static function virtualProviders(): Collection
    {
        return (new self)
            ->filterProviderType(self::PROVIDER_TYPE_VIRTUAL)
            ->where('is_deleted', self::IS_NOT_DELETED)
            ->get();
    }

    /**
     * 返回客户的产品类别
     *
     * @param $language_id
     * @param bool $is_pc_share_product
     * @return Collection
     */
    public function categoriesIsShowLatestPriority($language_id = ModelLanguage::LANGUAGE_ID_CN,
                                                   $is_pc_share_product = false): Collection
    {
        $categories = $this->categories()
            ->wherePivot('is_show', CategoryPivotCustomer::IS_SHOW)
            ->when($is_pc_share_product, function (Builder $query) {
                return $query->whereHas('products', function (Builder $query) {
                    return $query->where('customer_id', $this->id)
                        ->where('store_id', Product::STORE_ID_CUSTOMER)
                        ->where('is_sale', Product::IS_SALE);
                });
            })
            ->latest('category_pivot_customer.priority')
            ->with(['categoryDescriptions' => function (HasMany $query) use ($language_id) {
                $query->where('language_id', $language_id);
            }])
            ->get([
                'categories.id',
                'categories.has_asset',
                'categories.' . Language::getLocalNameColumn() // FIXME: 增加多语言后这里应该是没有用的吧
            ]);
        $categories->transform(function (Category $category) {
            $category->name = $category->categoryDescriptions->first()->name ?? null;

            return $category;
        });

        return $categories;
    }

    /**
     * 客户自己的品牌 with 系列， 用于复制产品时前端让客户自己选择新增还是复制到已有的品牌系列
     *
     * @return Brand[]|Collection
     */
    public function getBrandsWithSeries(): Collection
    {
        $customer_brands_with_series = (new Brand)->where('customer_id', $this->id)
            ->latest()
            ->select('id','name')
            ->with('brandSeries')
            ->get();

        return $customer_brands_with_series;
    }

    /**
     * 根据语言，返回客户开通的商品类别
     *
     * @param $language_id
     * @param null $has_asset
     * @return Category[]|Builder[]|Collection
     */
    public function getCustomerCategories($language_id, $has_asset = null)
    {
        $categories = $this->categories()
            ->when($has_asset, function($query){
                return $query->where('has_asset', Category::HAS_ASSET);
            })
            ->where('parent_id', Category::TOP_CATEGORY)
            ->with(['categoryDescriptions' => function ($query) use ($language_id) {
                $query->where('language_id', $language_id);
            }])
            ->get(['categories.id', 'categories.name', 'categories.has_asset']);

        $categories = $categories->sortByDesc(function ($category) {
            $category->name = $category->categoryDescriptions->first()->name ?? null;

            return $category->pivot->priority;
        })->values();

        return $categories;
    }

    /**
     * @param $language_id
     * @return Category[]|Builder[]|Collection
     */
    public function getAppCategories($language_id)
    {
        $categories = $this->categories()
            ->where('parent_id', Category::TOP_CATEGORY)
            ->with(['categoryDescriptions' => function ($query) use ($language_id) {
                $query->where('language_id', $language_id);
            }])
            ->get();

        $categories = $categories->map(function (Category $category) {
            $category->name =  $category->categoryDescriptions->first()->name ?? null;

            return $category;
        });

        return $categories;
    }

    /**
     * 客户是否应该显示收藏列表 （新鸿基定制）
     *
     * @param int $id
     *
     * @return bool
     */
    public static function isIdShowFavorProducts(int $id): bool
    {
        // 测试时使用，可设置对应客户不显示收藏商品
        $env_customer_ids = config('pkx.custom.customer_ids_not_show_favor');

        return ! in_array($id, array_merge($env_customer_ids, [self::ID_SUN_HUNG_KAI]));
    }

    /**
     * @return HasMany
     */
    public function couponTickets()
    {
        return $this->hasMany(CouponTicket::class);
    }
}
